#!/bin/bash
# Initial release date: October 2022
# Authors: @mativ00 & @sudoalx on Telegram
# Description: This bash script was created to automate the installation of images hosted on labhub.eu.org
# Version: 4.0.1-main
# Last Updated: 2025-07-03

# Default ishare2 configuration: this configuration will be overwritten when defined in the configuration file.
USE_ARIA2C=false       # Set to true to use aria2c for faster downloads. Set to false to use wget instead.
SSL_CHECK="true"       # Set to true to check SSL certificate. Set to false to disable SSL certificate check.
CHANNEL="dev"          # Set to main to use the main channel. Set to main to use the main channel.
JSON_URL=${MIRRORS[0]} # Set to the default mirror
# End of ishare2 configuration

# Initialize variables
ISHARE2_DIR="/opt/ishare2/cli"                                                                 # ishare2 directory
LOG_FILE="$ISHARE2_DIR/ishare2.log"                                                            # Log file
TEMP_JSON="$ISHARE2_DIR/labhub.json"                                                           # Temporary JSON file
VERSION="4.0.1-main"                                                                     # Current version of ishare2
SOURCES_URL="https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/sources.list"  # Sources.list URL
MOTD_FILE="$ISHARE2_DIR/motd.json"                                                             # MOTD file
REMOTE_MOTD_URL="https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/motd.json" # Remote MOTD URL

# Colors
RED='\033[31m'
YELLOW='\033[1;33m'
GREEN='\033[32m'
BLUE='\033[34m'
NO_COLOR='\033[0m'

# Script name
SCR_NAME_EXEC=$0
SCR_NAME_EXEC_FP=$(realpath "$0")
SCR_NAME=$(basename "$SCR_NAME_EXEC")
SCR_NAME=${SCR_NAME%.*}

# Configuration file
ISHARE2_CONF_FILE=$ISHARE2_DIR/ishare2.conf

logger() {
    local log_level=${1^^} # Convert log level to uppercase
    shift                  # Remove log level from arguments

    # Create LOG_FILE if it does not exist
    if [[ ! -f "$LOG_FILE" ]]; then
        touch "$LOG_FILE"
    fi

    # Ensure LOG_FILE is set and writable
    if [[ -z "$LOG_FILE" ]]; then
        echo "LOG_FILE is not set" >&2
        return 1
    elif [[ ! -w "$LOG_FILE" && ! -e "$LOG_FILE" ]]; then
        echo "Cannot write to LOG_FILE: $LOG_FILE" >&2
        # Attempt to create the log file
        touch "$LOG_FILE" || return 1
        # Attempt to fix the permissions
        chmod 644 "$LOG_FILE" || return 1
        return 1
    fi

    # Ensure SCR_NAME is set
    if [[ -z "$SCR_NAME" ]]; then
        SCR_NAME="UNKNOWN_SCRIPT"
    fi

    # Log the output to the log file
    echo -e "[$log_level][$SCR_NAME] $(date '+%Y-%m-%d %H:%M:%S'): $*" >>"$LOG_FILE"
}

# Function to fetch the latest list of sources needed for ishare2
ishare2_sources() {
    check_installed "curl"
    # Check if the sources.list file exists in the ishare2 directory
    if [ ! -f "$ISHARE2_DIR/sources.list" ]; then
        # If the sources.list file is older than 1 day, backup the file and download the latest sources.list
        if [ ! -f "$ISHARE2_DIR/sources.list" ] || [ $(find "$ISHARE2_DIR/sources.list" -mtime +1 -print) ]; then
            # Backup the sources.list file
            cp $ISHARE2_DIR/sources.list "$ISHARE2_DIR/sources.list.bak"
            # Download the latest sources.list
            curl -sL "$SOURCES_URL" >"$ISHARE2_DIR/sources.list"
            # Check if the sources.list file was downloaded successfully
            if [ $? -eq 0 ]; then
                logger info "The sources.list file has been downloaded successfully."
            else
                logger error "Failed to download the sources.list file."
                logger info "Restoring the backup sources.list file..."
                # Restore the backup sources.list file
                if [ -f "$ISHARE2_DIR/sources.list.bak" ]; then
                    cp "$ISHARE2_DIR/sources.list.bak" $ISHARE2_DIR/sources.list
                    logger info "The backup sources.list file has been restored."
                    echo -e "${RED}[-] Failed to download the sources.list file.${NO_COLOR}"
                    echo -e "${YELLOW}[!] Using the old sources.list file.${NO_COLOR}"
                    echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}"
                else
                    logger info "The backup sources.list file does not exist."
                    echo -e "${RED}[-] Failed to download the sources.list file.${NO_COLOR}"
                    echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}"
                    exit 1
                fi
            fi
        fi
    fi
    # Source the sources.list file
    source $ISHARE2_DIR/sources.list
}

# Function to initialize motd checks
check_motd() {
    # Fetch the latest MOTD from the remote source
    fetch_latest_motd

    # Check if the fetched MOTD is different from the current one
    if motds_are_different; then
        # Display the updated MOTD
        show_motd

        # Update the MOTD information
        update_motd_info
    else
        logger info "MOTD is up to date. No changes were made."
        if should_show_motd; then
            # Display the current MOTD
            show_motd
            # Update the MOTD information
            update_motd_info
        fi
    fi
}

# Function to check if the MOTD should be shown
should_show_motd() {
    current_date=$(date +"%Y-%m-%d")
    last_shown_date=$(jq -r '.last_shown_date' "$MOTD_FILE")
    show_count=$(jq -r '.show_count' "$MOTD_FILE")

    # Check if MOTD has not been shown today or shown less than 3 times
    if [ "$current_date" != "$last_shown_date" ] || [ "$show_count" -lt 3 ]; then
        return 0 # Show MOTD
    else
        return 1 # Do not show MOTD
    fi
}

# Function to display the MOTD
show_motd() {
    message=$(jq -r '.message' "$MOTD_FILE")
    messages=(
        "MOTD from the ishare2 team:"
        "$message"
        ""
        "Telegram: https://t.me/NetLabHub"
        "Donate: https://buymeacoffee.com/sudoalex"
        "GitHub: https://github.com/ishare2-org/ishare2-cli"
    )
    print_box "${messages[@]}"
}

# Function to update the MOTD information
update_motd_info() {
    current_date=$(date +"%Y-%m-%d")
    jq '.last_shown_date = $date | .show_count += 1' --arg date "$current_date" "$MOTD_FILE" >$ISHARE2_DIR/motd.tmp.json
    times_shown=$(jq -r '.show_count' "$ISHARE2_DIR/motd.tmp.json")
    logger info "MOTD has been shown $times_shown out of 3 times today."
    mv $ISHARE2_DIR/motd.tmp.json "$MOTD_FILE"
}

# Function to fetch the latest MOTD from a remote source
fetch_latest_motd() {
    if [ ! -f "$MOTD_FILE" ]; then
        logger info "MOTD file does not exist. Fetching the latest MOTD from the remote source."
        curl -s "$REMOTE_MOTD_URL" >$ISHARE2_DIR/motd.json
        return
    fi
    curl -s "$REMOTE_MOTD_URL" >$ISHARE2_DIR/motd.tmp.json
    # if curl failed then write custom message to motd file
    if [ $? -ne 0 ]; then
        echo -e "{\"message\":\"Failed to fetch the latest MOTD. Please check your internet connection.\"}" >$ISHARE2_DIR/motd.tmp.json
    fi
}

# Function to compare the current MOTD with the fetched MOTD
motds_are_different() {
    local current_motd
    local fetched_motd
    if [ ! -f "$MOTD_FILE" ] || [ ! -f "$ISHARE2_DIR/motd.tmp.json" ]; then
        return 0
    fi
    current_motd=$(jq -r '.message' "$MOTD_FILE")
    fetched_motd=$(jq -r '.message' "$ISHARE2_DIR/motd.tmp.json")
    # Compare the current MOTD with the fetched MOTD
    if [ "$current_motd" != "$fetched_motd" ]; then
        mv $ISHARE2_DIR/motd.tmp.json "$MOTD_FILE"
    fi

    [ "$current_motd" != "$fetched_motd" ]
}

# Function to check if there is a new version of ishare2 available
check_updates() {
    LATEST_VERSION=$(curl -sL https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/version)

    # Check if the version file could be fetched successfully
    if [ -z "$LATEST_VERSION" ]; then
        echo -e "${RED}[-] Failed to fetch the latest version information. Please check your internet connection.${NO_COLOR}"
        logger error "Failed to fetch the latest version information from: $LATEST_VERSION"
        return
    fi

    if [[ $LATEST_VERSION != $VERSION ]]; then
        # Make an array of messages
        messages=(
            "A newer version of ishare2 is available."
            "Current version: $VERSION"
            "Latest version: $LATEST_VERSION"
            "Run: ishare2 upgrade to update to the latest version."
            "See more methods to update on GitHub:"
            "https://github.com/ishare2-org/ishare2-cli"
        )
        print_box "${messages[@]}"
    fi
}

# Function to print a box-like component with adjusted width
print_box() {
    messages=("$@")

    # Split messages on line breaks and find the length of the longest line
    max_length=0
    IFS=$'\n'    # Internal Field Separator set to newline for splitting
    all_lines=() # Array to hold all lines of all messages
    for message in "${messages[@]}"; do
        # Split message into lines
        readarray -t lines <<<"$message"
        for line in "${lines[@]}"; do
            all_lines+=("$line")
            length=${#line}
            if ((length > max_length)); then
                max_length=$length
            fi
        done
    done
    IFS=$' \t\n' # Reset IFS to default

    # Display information in a box-like component with adjusted width
    echo -e "${YELLOW}┌$(printf '─%.0s' $(seq 1 $((max_length + 4))))┐${NO_COLOR}"
    for line in "${all_lines[@]}"; do
        printf "${YELLOW}│${NO_COLOR} %-$(($max_length + 2))s ${YELLOW}│${NO_COLOR}\n" "$line"
    done
    echo -e "${YELLOW}└$(printf '─%.0s' $(seq 1 $((max_length + 4))))┘${NO_COLOR}"
}

# Function to print a separator with a title
print_separator() {
    local TITLE=$1
    local WIDTH=${#TITLE}
    local SEPARATOR=""

    # Calculate the required number of '=' characters to create the separator
    for ((i = 0; i < WIDTH + 8; i++)); do
        SEPARATOR+="="
    done

    echo -e "${BLUE}$SEPARATOR${NO_COLOR}"
    echo -e "\033[1;33m    ${TITLE}    \033[0m"
    echo -e "${BLUE}$SEPARATOR${NO_COLOR}"
}

# Function to check if PNetLab is installed
is_pnetlab_installed() {
    # Check if pnetlab is installed
    data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null)
    data_array=("$data")
    pnetlab_version=${data_array[1]}
    if [[ -z $pnetlab_version ]]; then
        echo -e "${YELLOW}[!] WARN: PNetLab was not found.${NO_COLOR}"
        echo -e "${YELLOW}[!]${NO_COLOR} This script has only been tested on PNetLab. If you are not using PNetLab, you may encounter issues."
        logger warn "PNetLab is not installed."
        return 1
    fi
    return 0
}

# Function to initialize the ishare2 directory checks
check_ishare2_dir() {
    # check if ishare2 directory exists otherwise create it
    if [[ ! -d $ISHARE2_DIR ]]; then
        create_ishare2_dir
    fi
}

# Set max log file lines to 1000
set_max_log_lines() {
    if [[ -f $LOG_FILE ]]; then
        if [[ $(wc -l <"$LOG_FILE") -gt 1000 ]]; then
            tail -n 1000 $LOG_FILE >$ISHARE2_DIR/tmp.txt
            mv $ISHARE2_DIR/tmp.txt $LOG_FILE
        fi
    fi
}
create_default_conf() {
    install_dependencies
    # If the --init argument is passed, display a message to the user
    messages=(
        "The default configuration will be used. Feel free to modify it later"
        "by running: ishare2 config"
        "Press Enter to continue."
    )
    print_box "${messages[@]}"
    read -n 1

    # Write the default configuration to the configuration file
    echo "USE_ARIA2C=false" >$ISHARE2_CONF_FILE
    echo "SSL_CHECK=true" >>$ISHARE2_CONF_FILE
    echo "CHANNEL=$CHANNEL" >>$ISHARE2_CONF_FILE
    echo "ROTATE=true" >>$ISHARE2_CONF_FILE
    echo "JSON_URL=${MIRRORS[0]}" >>$ISHARE2_CONF_FILE

    # Display values used
    messages=(
        "Configuration values:"
        "USE_ARIA2C: false"
        "SSL_CHECK: true"
        "CHANNEL: $CHANNEL"
        "ROTATE: true"
    )
    print_box "${messages[@]}"
}

# Function to check if the configuration file exists, and validate it
check_config() {
    # get first argument
    local first_arg=$1
    if [[ $first_arg == "--init" ]]; then
        # check if ishare2 directory exists otherwise create it
        check_ishare2_dir
        # create default configuration
        create_default_conf
        return
    fi

    # check if config file exists
    if [[ ! -f $ISHARE2_CONF_FILE ]]; then
        create_config
    fi
    # check if config file is empty
    if [[ ! -s $ISHARE2_CONF_FILE ]]; then
        echo -e "${RED}[!] Configuration file is empty. Please configure ishare2.${NO_COLOR}"
        create_config
    fi
    # check if config file is valid
    validate_conf_file
}

get_available_mirrors() {
    # Ensure we have the latest index file
    fetch_json || {
        logger error "Failed to fetch index while getting mirrors"
        return 1
    }

    # Extract mirror names from the JSON structure
    local mirrors=()
    while IFS= read -r mirror_name; do
        mirrors+=("$mirror_name")
    done < <(jq -r '.url_properties.hostnames | keys[]' "$TEMP_JSON" 2>/dev/null)

    # Return default mirrors if none found
    if [ ${#mirrors[@]} -eq 0 ]; then
        logger error "No mirrors found in index."
        exit 1
    else
        echo "${mirrors[@]}"
    fi
}

# Function to create the configuration file
create_config() {
    clear
    messages=(
        "Welcome to ishare2!"
        "This script will guide you through the initial configuration process."
        "First, let's ensure all required dependencies are installed."
    )
    print_box "${messages[@]}"
    sleep 3

    is_pnetlab_installed

    if [[ -z $pnetlab_version ]]; then
        logger warn "PNetLab is not installed."
    else
        echo -e "${GREEN}[✓] Detected PNetLab version: $pnetlab_version${NO_COLOR}"
        fix_repositories
        check_pnetlab_sources
    fi

    echo -e "${BLUE}[i] Installing iShare2 dependencies...${NO_COLOR}"
    MAX_RETRIES=3
    retry_counter=0

    while [[ $retry_counter -lt $MAX_RETRIES ]]; do
        if install_dependencies; then
            break
        fi
        echo -e "${RED}[✗] Dependency installation failed. Retrying... (${retry_counter}/${MAX_RETRIES})${NO_COLOR}"
        logger error "Failed to install dependencies. Retrying..."
        retry_counter=$((retry_counter + 1))
        sleep 5
    done

    if [[ $retry_counter -eq $MAX_RETRIES ]]; then
        MAX_RETRIES=3
        retry_counter=0
        while [[ $retry_counter -lt $MAX_RETRIES ]]; do
            if check_pnetlab_sources; then
                break
            fi
            echo -e "${RED}[✗] Could not update PNetLab sources. Retrying... (${retry_counter}/${MAX_RETRIES})${NO_COLOR}"
            logger error "Failed to replace PNetLab sources. Retrying..."
            retry_counter=$((retry_counter + 1))
            sleep 5
        done
    fi

    logger info "Launching configuration wizard..."
    clear
    messages=(
        "ishare2 Configuration Wizard"
        "- This wizard will guide you through essential setup steps."
        "- Press Enter to accept default values when applicable."
        "- To reconfigure later, run: ishare2 config"
        "- Press Ctrl+C at any time to cancel."
    )
    print_box "${messages[@]}"
    sleep 3

    echo -e "${YELLOW}[?] Enable aria2c for accelerated downloads? (default: no)${NO_COLOR}"
    read -p "[y/N]: " -r
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        echo -e "${YELLOW}[!] aria2c can improve download speeds, but may cause issues for some users."
        echo "To disable later, run: ishare2 config and answer no to this question.${NO_COLOR}"
        echo "USE_ARIA2C=true" >"$ISHARE2_CONF_FILE"
        install_package "aria2"
        read -n 1
    else
        echo "USE_ARIA2C=false" >"$ISHARE2_CONF_FILE"
    fi

    echo -e "${YELLOW}[?] Perform SSL certificate validation for downloads? (default: yes)${NO_COLOR}"
    read -p "[y/N]: " -r
    case $REPLY in
    [Nn]) SSL_CHECK=false ;;
    *) SSL_CHECK=true ;;
    esac
    echo "SSL_CHECK=\"$SSL_CHECK\"" >>"$ISHARE2_CONF_FILE"

    echo -e "${YELLOW}[?] Select update channel (branch).${NO_COLOR}"
    mapfile -t branches < <(curl -sL https://api.github.com/repos/ishare2-org/ishare2-cli/branches | jq -r '.[].name')
    for ((i = 0; i < ${#branches[@]}; i++)); do
        echo " $((i + 1))) ${branches[i]}"
    done

    local current_channel=$CHANNEL
    read -p "[*] Enter the number of the desired branch (default: $CHANNEL): " -r

    if [[ -z $REPLY ]]; then
        echo -e "${YELLOW}[i] Default branch selected: $CHANNEL${NO_COLOR}"
    elif ! [[ $REPLY =~ ^[0-9]+$ ]] || ((REPLY < 1 || REPLY > ${#branches[@]})); then
        echo -e "${RED}[✗] Invalid input. Falling back to default branch: $CHANNEL${NO_COLOR}"
    else
        CHANNEL=${branches[REPLY - 1]}
        echo -e "${GREEN}[✓] Branch selected: $CHANNEL${NO_COLOR}"
        echo -e "${BLUE}[i] Running 'ishare2 upgrade' to switch to this branch.${NO_COLOR}"
    fi
    echo "CHANNEL=\"$CHANNEL\"" >>"$ISHARE2_CONF_FILE"

    local available_mirrors=($(get_available_mirrors))
    [[ ${#available_mirrors[@]} -eq 0 ]] && available_mirrors=("drive" "main")

    declare -A mirror_descriptions=(
        ["drive"]="Google Drive mirror"
        ["main"]="OneDrive mirror"
        ["backup"]="Backup server"
        ["cdn"]="Content Delivery Network"
    )

    echo -e "\n${YELLOW}[!] Mirror Selection${NO_COLOR}"
    echo "iShare2 can download files from multiple mirror sources."
    echo
    echo " 1) Automatically rotate mirrors for reliability"

    local counter=2
    declare -A mirror_options
    for mirror in "${available_mirrors[@]}"; do
        local desc="${mirror_descriptions[$mirror]:-$mirror mirror}"
        echo " $counter) Always use: $desc"
        mirror_options[$counter]=$mirror
        ((counter++))
    done

    echo
    read -p "[?] Enter your choice (default: 1): " -r choice
    choice=${choice:-1}

    case $choice in
    1)
        echo -e "${GREEN}[✓] Mirror rotation enabled (${#available_mirrors[@]} sources).${NO_COLOR}"
        echo "ROTATE=true" >>"$ISHARE2_CONF_FILE"
        ;;
    *)
        if [[ -n "${mirror_options[$choice]}" ]]; then
            local selected_mirror="${mirror_options[$choice]}"
            local selected_desc="${mirror_descriptions[$selected_mirror]:-$selected_mirror mirror}"
            echo -e "${GREEN}[✓] Mirror locked to: $selected_desc${NO_COLOR}"
            echo "ROTATE=false" >>"$ISHARE2_CONF_FILE"
            echo "MIRROR=\"$selected_mirror\"" >>"$ISHARE2_CONF_FILE"
        else
            echo -e "${RED}[✗] Invalid choice. Mirror rotation will be used.${NO_COLOR}"
            echo "ROTATE=true" >>"$ISHARE2_CONF_FILE"
        fi
        ;;
    esac

    echo >>"$ISHARE2_CONF_FILE"

    if [[ ! -f "$ISHARE2_CONF_FILE" ]]; then
        echo -e "${RED}[✗] Failed to create configuration file.${NO_COLOR}"
        logger error "Configuration file creation failed."
        exit 1
    fi

    remove_duplicates
    validate_conf_file

    messages=(
        "Configuration completed successfully."
        "You can now start using ishare2."
        "[!] IMPORTANT:"
        "- ishare2 is a free, open-source tool. If someone charged you for it, you've been scammed."
        "- Avoid unofficial sources. They may contain malicious code."
    )
    print_box "${messages[@]}"

    if [[ $current_channel != $CHANNEL ]]; then
        echo -e "${YELLOW}[i] Updating ishare2 to the selected '$CHANNEL' branch...${NO_COLOR}"
        curl -sL "https://raw.githubusercontent.com/ishare2-org/ishare2-cli/$CHANNEL/ishare2" >/usr/sbin/ishare2
        chmod +x /usr/sbin/ishare2
        echo -e "${GREEN}[✓] iShare2 has been updated to the latest version from the '$CHANNEL' branch.${NO_COLOR}"
    fi
}

# Function to remove duplicate lines from the configuration file
remove_duplicates() {
    local config_file="$ISHARE2_CONF_FILE"

    # Check if the configuration file exists
    if [ ! -f "$config_file" ]; then
        echo -e "${RED}[!] Configuration file does not exist.${NO_COLOR}"
        logger error "Configuration file does not exist."
        return
    fi

    # Remove duplicate lines from the configuration file
    awk '!seen[$0]++' "$config_file" >"$config_file.tmp" && mv "$config_file.tmp" "$config_file"

    logger info "Removed duplicate lines from the configuration file."
}

# Function to validate the configuration file
validate_conf_file() {

    # Check if the configuration file exists
    if [ ! -f "$ISHARE2_CONF_FILE" ]; then
        echo -e "${RED}[!] Configuration file does not exist.${NO_COLOR}"
        logger error "Configuration file does not exist."
        return 1
    fi

    # Array of allowed variables
    local allowed_vars=("USE_ARIA2C" "CHANNEL" "SSL_CHECK" "ROTATE" "MOTD")

    # Read the configuration file line by line and filter out invalid variables
    local filtered_content=""
    while IFS= read -r line; do
        # Skip empty lines and comments
        if [[ -z "$line" || "$line" == \#* ]]; then
            continue
        fi

        # Extract variable name by trimming leading and trailing whitespaces
        local var_name="$(echo "$line" | awk -F '=' '{gsub(/^[ \t]+|[ \t]+$/, "", $1); print $1}')"

        # Check if the variable is allowed
        is_allowed=false
        for allowed_var in "${allowed_vars[@]}"; do
            if [[ "$allowed_var" == "$var_name" ]]; then
                is_allowed=true
                break
            fi
        done

        if $is_allowed || [[ "$line" != *=* ]]; then
            filtered_content+="$line"$'\n'
        else
            logger warn "Removed invalid variable \"$var_name\" from the configuration file."
        fi
    done <"$ISHARE2_CONF_FILE"

    # Write the filtered content back to the configuration file
    echo "$filtered_content" >"$ISHARE2_CONF_FILE"

    logger info "Validated the configuration file."
    # shellcheck source=/opt/ishare2/cli/ishare2.conf
    source "$ISHARE2_CONF_FILE"
    return 0
}

# Function to remove i-share.top apt sources.list
remove_ishare_sources() {
    # Remove i-share.top line from sources.list
    sed -i '/i-share.top/d' /etc/apt/sources.list
}

# Function to install dependencies required for ishare2
install_dependencies() {
    remove_ishare_sources
    echo -e "${YELLOW}[+] ishare2 needs to install some dependencies to work properly.${NO_COLOR}"
    local packages=("curl" "wget" "jq" "unrar" "tree" "unzip")
    echo -e "${YELLOW}[+] List of packages to be installed: ${NO_COLOR}"
    for package in "${packages[@]}"; do
        echo -e "${YELLOW} -${NO_COLOR} $package"
    done
    echo -e "${YELLOW}[+] Starting to install dependencies in 5 seconds... This may take a while. Press Ctrl+C to cancel.${NO_COLOR}"
    sleep 5

    for package in "${packages[@]}"; do
        install_package "$package"
    done
}

# Function to check if a package is installed and install it if it's not
check_installed() {
    PACKAGE=$1
    # Check if package is installed
    if ! command -v "$PACKAGE" &>/dev/null; then
        echo -e "${YELLOW}[!] WARN: $PACKAGE is not installed.${NO_COLOR}"
        install_package "$PACKAGE"
        # Check success of package installation
        if ! command -v "$PACKAGE" &>/dev/null; then
            echo -e "${RED}[-] Error: Failed to install $PACKAGE.${NO_COLOR}"
            logger error "Failed to install $PACKAGE."
            exit 1
        else # package is installed
            echo -e "${GREEN}[+] $PACKAGE has been installed successfully.${NO_COLOR}"
        fi
    fi
}

# Function to check if a site is accessible
is_site_accessible() {
    local url=$1
    if ! curl -s --head --request GET "$url" | grep "200 OK\|301 Moved Permanently" >/dev/null; then
        return 1 # Site is not accessible
    else
        return 0 # Site is accessible
    fi
}

# Function to check if the sources.list file is using the correct sources
fix_repositories() {
    # Immediately check for http://repo.pnetlab.com and replace it with https if found
    if grep -q "deb \[trusted=yes\] http://repo.pnetlab.com ./" /etc/apt/sources.list; then
        echo -e "${YELLOW}[+] Found http://repo.pnetlab.com in sources.list, replacing with https...${NO_COLOR}"
        sed -i 's|deb \[trusted=yes\] http://repo.pnetlab.com ./|deb [trusted=yes] https://repo.pnetlab.com ./|g' /etc/apt/sources.list
    fi

    # Check if https://repo.pnetlab.com is accessible
    if ! is_site_accessible "https://repo.pnetlab.com"; then
        echo -e "${YELLOW}[!] repo.pnetlab.com is not accessible.${NO_COLOR}"
        echo -e "${YELLOW}[!] Checking alternative...${NO_COLOR}"
        # The script previously modified any http to https if necessary, so just check for https existence now
        if grep -q "deb \[trusted=yes\] https://repo.pnetlab.com ./" /etc/apt/sources.list; then
            echo -e "${YELLOW}[!] Your sources.list is already using https for repo.pnetlab.com.${NO_COLOR}"
        fi
        # Check if https://pnetlab.labhub.eu.org is accessible
        if ! is_site_accessible "https://pnetlab.labhub.eu.org"; then
            echo -e "${RED}[-] Both repo.pnetlab.com and the alternative pnetlab.labhub.eu.org are inaccessible.${NO_COLOR}"
            echo -e "${YELLOW}[!] Please check your internet connection and try again.${NO_COLOR}"
            # Ask the user if they want to continue anyway
            echo -e "${YELLOW}[?] Do you want to continue anyway?${NO_COLOR}"
            read -p "[?] (y/n): " -r
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                exit 1
            fi
        fi
        # Ask the user if they want to replace the sources
        echo -e "${YELLOW}[!] The official pnetlab.com repository is not accessible.${NO_COLOR}"
        echo -e "${YELLOW}[?] Do you want to replace the pnetlab sources with the LabHub mirror?${NO_COLOR}"
        read -p "[?] (y/n): " -r
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            # Comment out "deb [trusted=yes] https://repo.pnetlab.com ./" and add "deb [trusted=yes] https://pnetlab.labhub.eu.org ./"
            echo -e "${YELLOW}[+] Replacing pnetlab sources with labhub mirror...${NO_COLOR}"
            sed -i 's|deb \[trusted=yes\] https://repo.pnetlab.com ./|#deb \[trusted=yes\] https://repo.pnetlab.com ./|g' /etc/apt/sources.list
            echo "deb [trusted=yes] https://pnetlab.labhub.eu.org ./" >>/etc/apt/sources.list
        fi
    else
        echo -e "${GREEN}[+] repo.pnetlab.com is accessible.${NO_COLOR}"
        # No further action needed if the site is accessible, assuming the earlier check already ensured https is used
    fi
}

# Function to run apt-get update
apt_get_update() {
    echo -e "${YELLOW}[+] Updating package lists...${NO_COLOR}"
    if ! apt-get update -y -qq; then
        return 1
    fi
}

# Function to install a package
install_package() {
    local package_name=$1
    # check if $package_name is installed otherwise install it
    if ! command -v "$package_name" &>/dev/null; then
        echo -e "${YELLOW}[+] $package_name is not installed.${NO_COLOR}"
        if ! apt_get_update; then
            echo -e "${RED}[-] Failed to update package lists.${NO_COLOR}"
            echo -e "${YELLOW}[!] Please check your internet connection and try again or fix your repositories.${NO_COLOR}"
            logger error "Failed to update package lists."
        fi

        echo -e "${YELLOW}[+] Installing $package_name...${NO_COLOR}"
        if ! apt-get install -qq -y "$package_name"; then
            echo -e "${RED}[-] Failed to install $package_name. Please install it manually or you will likely encounter issues in the future.${NO_COLOR}"
            echo -e "${YELLOW}[!] Run: apt-get install -y $package_name${NO_COLOR}"
            logger error "Failed to install $package_name."
            exit 1
        fi
    else
        echo -e "${GREEN}[+] $package_name is already installed.${NO_COLOR}"
        logger info "Package $package_name is already installed."
        logger info "Updating $package_name..."
        if ! apt-get install --only-upgrade -qq "$package_name"; then
            logger error "Failed to update $package_name."
            exit 1
        fi
    fi
}

# Function to check if APT or dpkg is running and unlock dpkg
unlock_dpkg() {
    # Check if APT or dpkg is running
    if pgrep -f '^[/]usr[/]bin[/](apt|dpkg)' >/dev/null 2>&1; then
        echo "APT or dpkg seems to be running in the background."
        echo -e "${YELLOW}[?] Do you want to unlock dpkg?${NO_COLOR}"

        # Confirm with the user
        echo -e "${YELLOW}[!] Warning: Unlocking dpkg while APT or dpkg processes are active can lead to a broken system.${NO_COLOR}"
        echo "Proceed with caution. It's recommended to try this only if you're sure that no APT or dpkg processes are running."
        echo "Proceed? (y/n)"
        read -r answer

        if [[ $answer != "y" && $answer != "Y" ]]; then
            echo "Operation cancelled by the user."
        fi

        # Attempt to remove lock files
        echo "Attempting to remove lock files..."
        local lock_files=(
            "/var/lib/dpkg/lock"
            "/var/cache/apt/archives/lock"
            "/var/lib/apt/lists/lock"
            "/var/lib/dpkg/lock-frontend"
        )

        for file in "${lock_files[@]}"; do
            if [ -f "$file" ]; then
                sudo rm "$file" >/dev/null 2>&1
                echo "Removed $file"
            else
                echo "$file does not exist or has already been removed."
            fi
        done

        # Attempt to reconfigure dpkg
        echo "Reconfiguring dpkg..."
        sudo dpkg --configure -a

        echo "Operation completed. Please try running your APT or dpkg command again."
    fi
}

# Function to create the ishare2 directory
create_ishare2_dir() {
    echo -e "${YELLOW}[!] Creating ishare2 directory...${NO_COLOR}"
    if ! mkdir -p $ISHARE2_DIR 2>/dev/null; then
        error_message=$(mkdir -p $ISHARE2_DIR 2>&1 >/dev/null)
        if [[ $error_message == *"Permission denied"* ]]; then
            echo -e "${RED}[-] Failed to create ishare2 directory: Permission denied.${NO_COLOR}"
        else
            echo -e "${RED}[-] Failed to create ishare2 directory. Error: $error_message${NO_COLOR}"
        fi
        exit 1
    fi
    logger info "ishare2 directory created successfully."
}

check_image_hash() {
    local image_path=$1
    local image_name=$2
    local expected_hash=$3
    local hash_type=${4,,} # Convert to lowercase (e.g., "SHA1" → "sha1")

    logger info "${YELLOW}[+] Checking ${hash_type^^} hash for ${image_name}${NO_COLOR}"

    if [[ -z "$expected_hash" ]]; then
        logger info "${YELLOW}[!] No ${hash_type^^} hash provided in the index. Skipping check.${NO_COLOR}"
        return
    fi

    if [ ! -f "$image_path/$image_name" ]; then
        logger error "${RED}[-] File not found: $image_path/$image_name${NO_COLOR}"
        return 1
    fi

    local actual_hash
    case "$hash_type" in
    sha1)
        actual_hash=$(sha1sum "$image_path/$image_name" | awk '{print $1}')
        ;;
    md5)
        actual_hash=$(md5sum "$image_path/$image_name" | awk '{print $1}')
        ;;
    *)
        echo -e "${RED}[-] Unsupported hash type: $hash_type${NO_COLOR}"
        return 1
        ;;
    esac

    if [[ "$actual_hash" != "$expected_hash" ]]; then
        echo -e "${RED}[-] ${hash_type^^} mismatch for $image_name${NO_COLOR}"
        echo -e "${YELLOW} Expected: $expected_hash${NO_COLOR}"
        echo -e "${YELLOW} Actual  : $actual_hash${NO_COLOR}"
        return 1
    fi

    echo -e "${GREEN}[✓] ${hash_type^^} hash is valid.${NO_COLOR}"
}

# Function to check image size against the downloaded image
check_image_size() {
    local image_path=$1
    local image_name=$2
    local image_size_from_json=$3

    # Check if the image file exists
    if [ ! -f "$image_path/$image_name" ]; then
        echo -e "${RED}[-] Error: The image file was not found at $image_path/$image_name while checking its size.${NO_COLOR}"
        logger error "The image file was not found: $image_path/$image_name"
        return 1
    fi

    local image_size
    image_size=$(stat -c%s "$image_path/$image_name" 2>/dev/null)

    # Validate image_size is numeric
    if [[ -z "$image_size" || ! "$image_size" =~ ^[0-9]+$ ]]; then
        echo -e "${RED}[-] Error: Unable to determine image size for $image_path/$image_name.${NO_COLOR}"
        logger error "Invalid or missing image size for: $image_path/$image_name"
        return 1
    fi

    # Check if the image size is less than 20KB
    if [ $image_size -lt 20000 ]; then
        echo -e "${RED}[-] Error: The image file size is too small. The file may be corrupted.${NO_COLOR}"
        logger error "The image file size is too small: $image_path/$image_name. Expected size: $image_size_from_json. Actual size: $image_size"
        return 1
    fi

    # Check if the image size matches the size from the JSON file
    if [[ -z "$image_size_from_json" || ! "$image_size_from_json" =~ ^[0-9]+$ ]]; then
        echo -e "${RED}[-] Error: Invalid or missing expected size from JSON for $image_name.${NO_COLOR}"
        logger error "Missing or non-numeric image_size_from_json: $image_size_from_json"
    fi

    if [ "$image_size" -ne "$image_size_from_json" ]; then
        echo -e "${RED}[-] Error: The image file size does not match the size expected by the index.${NO_COLOR}"
        echo -e "${RED}[!] Debug information:${NO_COLOR}"
        echo -e "${YELLOW} Expected size: $image_size_from_json${NO_COLOR}"
        echo -e "${YELLOW} Actual size: $image_size${NO_COLOR}"
        echo -e "${YELLOW} JSON file URL: $JSON_URL${NO_COLOR}"
        logger error "Mismatch: expected $image_size_from_json, got $image_size ($image_path/$image_name)"
    fi

    echo -e "${GREEN}[✓] The downloaded image is the expected size.${NO_COLOR}"
}

fetch_json() {
    check_installed "jq"

    # Constants
    REPO_URL="https://api.github.com/repos/ishare2-org/mirrors/releases/latest"

    logger info "Fetching the latest release information from: $REPO_URL"

    # Fetch release metadata quietly
    response=$(curl -sL "$REPO_URL") || {
        logger error "Failed to fetch release metadata from: $REPO_URL"
        exit 1
    }

    # Extract labhub.json URL
    labhub_url=$(echo "$response" | jq -r '.assets[] | select(.name == "labhub.json") | .browser_download_url')

    if [[ -z "$labhub_url" ]]; then
        logger error "labhub.json not found in release metadata from: $REPO_URL"
        echo -e "${RED}[-] Error: Failed to get image index URL from the LabHub repository.${NO_COLOR}"
        exit 1
    fi

    logger info "Downloading labhub.json from: $labhub_url"
    if curl -sSL -o "$TEMP_JSON" "$labhub_url"; then
        logger info "labhub.json downloaded successfully."
    else
        logger error "Failed to download labhub.json from: $labhub_url"
        echo -e "${RED}[-] Failed to download the JSON index file. Check your internet connection.${NO_COLOR}"
        echo -e "${YELLOW}[!] Running connection test...${NO_COLOR}"

        connection_tests
        local connection_test_result=$?

        if [[ $connection_test_result -eq 0 ]]; then
            echo -e "${GREEN}[+] Internet connection is working.${NO_COLOR}"
            echo -e "${YELLOW}[?] Do you want to reconfigure ishare2?${NO_COLOR}"
            read -p "[?] (y/n): " -r
            if [[ $REPLY =~ ^[Yy]$ ]]; then
                echo -e "${YELLOW}[+] Reconfiguring ishare2...${NO_COLOR}"
                create_config
            else
                echo -e "${RED}[-] Check the log file for more information.${NO_COLOR}"
                echo -e "${YELLOW}[!] Log file: $LOG_FILE${NO_COLOR}"
            fi
        else
            echo -e "${RED}[-] Connection test failed. Please check your internet connection.${NO_COLOR}"
        fi
        exit 1
    fi

    # Validate downloaded JSON
    if ! jq -e . <"$TEMP_JSON" >/dev/null 2>&1; then
        logger error "Invalid JSON structure in: $TEMP_JSON"
        echo -e "${RED}[-] Error: Invalid JSON index file.${NO_COLOR}"
        logger info "Debug information: The downloaded JSON file is not valid. Please check the file at: $TEMP_JSON"
        exit 1
    else
        logger info "Valid JSON file confirmed: $TEMP_JSON"
    fi
}

# Function to download a file using wget or aria2c
download_file() {
    local url="$1"
    local output="$2"
    local options="$3"
    USE_ARIA2C=$(grep "USE_ARIA2C" $ISHARE2_CONF_FILE | cut -d "=" -f2)
    logger info "Downloading file from $url to $output with options: $options"

    if [[ $USE_ARIA2C == "true" ]]; then
        # Check if aria2c is installed
        if ! command -v aria2c &>/dev/null; then
            echo -e "${RED}[-] Error: aria2c is not installed. Attempting to install it...${NO_COLOR}"
            install_package "aria2" >/dev/null
            # Check if aria2c is installed
            if ! command -v aria2c &>/dev/null; then
                echo -e "${RED}[-] Error: aria2c is not installed. Please install it manually and try again.${NO_COLOR}"
                exit 1
            else # aria2c is installed
                echo -e "${GREEN}[+] aria2c has been installed successfully.${NO_COLOR}"
            fi
        fi
    fi

    if command -v aria2c &>/dev/null && [[ $USE_ARIA2C == "true" ]]; then
        download_file_aria2 "$url" "$output" "$options"
    else
        download_file_wget "$url" "$output"
    fi
}

# Function to download a file using aria2c
download_file_aria2() {
    local url="$1"
    local output_dir="$2"
    local output_name="$3"

    logger info "Downloading file from $url..."
    rm -f error.log
    if [ "$SSL_CHECK" = true ]; then
        aria2c --console-log-level=warn -j 2 -c --split=4 --min-split-size=20M --max-connection-per-server=4 --download-result=hide --allow-overwrite=true --dir="$output_dir" --out="$output_name" --log="error.log" "$url"
    else
        aria2c --console-log-level=warn -j 2 -c --split=4 --min-split-size=20M --max-connection-per-server=4 --download-result=hide --check-certificate=false --allow-overwrite=true --dir="$output_dir" --out="$output_name" --log="error.log" "$url"
    fi

    if [ $? -eq 0 ]; then
        # line break
        echo
        echo -e "${YELLOW}[+] ${GREEN}The file has been downloaded successfully to $output_dir$output_name ${NO_COLOR}"
        rm -f error.log
    else
        echo -e "${RED}[-] Error: Unable to download the file. Check the error.log file for more information.${NO_COLOR}"
        exit 1
    fi
    # Check if file size is less than 20KB and its extension is not excluded
    local excluded_extensions=("html" "txt" "php" "yml" "md" "json" "sh" "yaml" "txt")

    if [ -f "$output_dir$output_name" ] && [ $(stat -c%s "$output_dir$output_name") -lt 20000 ]; then
        is_excluded=false
        for ext in "${excluded_extensions[@]}"; do
            if [ "${output_name##*.}" = "$ext" ]; then
                is_excluded=true
                break
            fi
        done

        if [ "$is_excluded" = false ]; then
            echo -e "${RED}[-] The file size is too small. The file may be corrupted.${NO_COLOR}"
            # Check if html file is downloaded instead of the actual file, key words: "DOCTYPE", "<html>", Bhadoo
            if grep -q "DOCTYPE" "$output_dir$output_name" || grep -q "<html>" "$output_dir$output_name" || grep -q "Bhadoo" "$output_dir$output_name"; then
                echo -e "${RED}[-] The file may be corrupted. The file may be an HTML file instead of the actual file.${NO_COLOR}"
            fi
            # Show troubleshooting steps
            echo -e "${YELLOW}[!] Troubleshooting:${NO_COLOR}"
            echo -e "${YELLOW}[!] 1) Switch mirrors, if you have mirrors rotation enabled then run the command again using the --overwrite flag.${NO_COLOR}"
            echo -e "${YELLOW}[!] 2) If the issue persists, visit the live repository through your web browser and download the file manually.${NO_COLOR}"
            echo -e "${YELLOW}[!] 3) If the issue persists, report the issue to the ishare2 team on Telegram.${NO_COLOR}"
        fi
    fi

}

# Function to download a file using curl
download_file_curl() { # $1 = url, $2 = output
    # Create the directory if it does not exist
    mkdir -p $ISHARE2_DIR/tmp
    # Create the file if it does not exist
    touch $ISHARE2_DIR/tmp/curl_error
    local url="$1"
    local output="$2"
    logger info "Downloading file from $url..."
    if [ "$SSL_CHECK" = true ]; then
        curl -s -o "$output" "$url" 2>$ISHARE2_DIR/tmp/curl_error
    else
        curl -s --insecure -o "$output" "$url" 2>$ISHARE2_DIR/tmp/curl_error
    fi

    if [ $? -eq 0 ]; then
        logger info "The file has been downloaded successfully to $output."
        rm -f $ISHARE2_DIR/tmp/curl_error
    else
        echo -e "${RED}[-] Error: Unable to download the file. ${NO_COLOR}"
        # Read the error message from the curl_error file
        ERROR_MESSAGE=$(cat $ISHARE2_DIR/tmp/curl_error)
        # Print the error message
        echo -e "${RED}[-] Logs:\n $ERROR_MESSAGE ${NO_COLOR}"
        exit 1
    fi
}

# Function to download a file using wget
download_file_wget() {
    # Create the directory if it does not exist
    mkdir -p $ISHARE2_DIR/tmp
    # Create the file if it does not exist
    touch $ISHARE2_DIR/tmp/wget_error
    local url="$1"
    local output_dir="$2"
    local output_name="$3"
    if [[ -z "$output_name" ]]; then
        # if output name is not provided, use the basename of the URL
        output_name=$(basename "$url")
    fi
    local output="$output_dir/$output_name"
    logger info "Downloading file from $url..."

    if [ "$SSL_CHECK" = true ]; then
        wget -O "$output" "$url" -q --show-progress --content-disposition -T 300
    else
        wget -O "$output" "$url" -q --show-progress --content-disposition --no-check-certificate -T 300
    fi

    # Check if wget command failed
    if [ $? -eq 0 ]; then
        logger info "The file has been downloaded successfully to $output."
        rm -f $ISHARE2_DIR/tmp/wget_error
    else
        echo -e "${RED}[-] Error: Unable to download the file. ${NO_COLOR}"
        # Read the error message from the wget_error file
        ERROR_MESSAGE=$(cat $ISHARE2_DIR/tmp/wget_error)
        # Print the error message
        echo -e "${RED}[-] Read the logs above for more information. ${NO_COLOR}"
        logger error "Failed to download the file. Error: $ERROR_MESSAGE"
        exit 1
    fi
}

# Function to check if the user is root
check_user_is_root() {
    if ! [[ "$(id -u)" == 0 ]]; then
        user=$(whoami)
        echo -e "${RED}[!] This script requires root privileges to be executed. The current user, \"$user\", does not have enough permissions. Please, switch to the root user to run the script.${NO_COLOR}"
        exit 1
    fi
}

# Function to get the yaml templates directory based on the server platform
get_server_platform() {
    grep -q vmx /proc/cpuinfo && echo -n vmx >/opt/unetlab/platform
    grep -q svm /proc/cpuinfo && echo -n svm >/opt/unetlab/platform

    platform=$(cat /opt/unetlab/platform)

    if [[ $platform == "vmx" ]]; then YML_DIR="/opt/unetlab/html/templates/intel/"; fi
    if [[ $platform == "svm" ]]; then YML_DIR="/opt/unetlab/html/templates/amd/"; fi
}

# Function to set the yaml templates directory based on the PNETLab version
set_yml_template_folder_location() {
    data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null)
    data_array=("$data")
    pnetlab_version=${data_array[1]}

    if [[ $pnetlab_version == "4.2.10" ]] || [[ $pnetlab_version == "5.0.1" ]]; then
        YML_DIR="/opt/unetlab/html/templates/"
    fi

    if [[ $pnetlab_version == *5.2* ]] || [[ $pnetlab_version == *5.3* ]] || [[ $pnetlab_version == *6.* ]]; then
        get_server_platform
    fi
}

# Function to list the labs available in the default or custom labs directory
list_labs() {
    # Check if the user passed a custom path for Labs directory
    if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs"
        LABS_DIR=$2
        CUSTOM_PATH_FLAG=1
        RELATIVE_PATH=$LABS_DIR
        if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute
            # Convert the path to absolute
            LABS_DIR=$(realpath "$LABS_DIR")
            # Check if the path is a directory and add '/' if it's missing
            if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then
                LABS_DIR="$LABS_DIR/"
            fi
        fi
        NOT_FOUND_MESSAGE="Make sure you are passing the correct path to the folder where your labs are located"
        echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}"
    else # Use default path for PNETLab Store
        LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/"
        NOT_FOUND_MESSAGE="This folder is automatically created when a lab from PNETLab Store is downloaded"
    fi

    if ! [[ -d $LABS_DIR ]]; then
        echo "\"$LABS_DIR\" directory has not been found"
        echo "$NOT_FOUND_MESSAGE"
        exit 0
    fi

    if [[ -z "$(ls -A "$LABS_DIR")" ]]; then
        echo "No labs found in \"$LABS_DIR\" directory"
        exit 0
    fi

    counter_all_labs_installed_on_server=0
    for file in "$LABS_DIR"*; do
        counter_all_labs_installed_on_server=$((counter_all_labs_installed_on_server + 1))
    done

    echo -e "Labs currently available in \"$RELATIVE_PATH\" directory: $counter_all_labs_installed_on_server"

    declare -a readableLabsArray=()
    declare -a authorLabsArray=()

    for file in "$LABS_DIR"*; do
        check=$(grep -Pio 'encoding="UTF-8"' "$file")
        SUB="encoding"
        if [[ $check == *"$SUB"* ]]; then
            readableLabsArray+=("$file")
            author_lab_name=$(grep -Pio '.*author="\K[^"]*' "$file" | sort -u)
            if [[ -z $author_lab_name ]]; then # Author name not specified
                author_lab_name=N/A
            fi
            authorLabsArray+=("$author_lab_name")
        fi
    done

    echo "Labs in plain text: ${#readableLabsArray[@]}"

    if [[ ${#readableLabsArray[@]} -eq 0 ]]; then
        STR="No readable labs were found, so images cannot be downloaded for any lab"
        echo -e "${RED}[-]$STR\033[0m"
        exit 0
    fi

    echo -e "\nList\n"

    counter_for_readable_labs=0

    for f in "${readableLabsArray[@]}"; do
        counter_for_readable_labs=$((counter_for_readable_labs + 1))
        echo -e "$counter_for_readable_labs) ${f##*/} - ${authorLabsArray[$((counter_for_readable_labs - 1))]}"
    done
    if [[ $CUSTOM_PATH_FLAG -eq 1 ]]; then
        CMD_TO_EXECUTE="ishare2 mylabs $RELATIVE_PATH <number>"
        CMD_TO_EXECUTE_ALL="ishare2 mylabs $RELATIVE_PATH all"
    else
        CMD_TO_EXECUTE="ishare2 labs <number>"
        CMD_TO_EXECUTE_ALL="ishare2 labs all"
    fi
    echo -e "\nTo install images for a specific lab, use the following command:\n\033[32m$CMD_TO_EXECUTE <number>\033[0m"
    echo -e "\nTo install images for all of the labs, use the following command:\n\033[32m$CMD_TO_EXECUTE_ALL\033[0m"
}

# Function to pull images for a lab
install_images_from_lab() {
    # Check if the user passed a custom path for Labs directory
    if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs"
        LABS_DIR=$2
        RELATIVE_PATH=$LABS_DIR
        if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute
            # Convert the path to absolute
            LABS_DIR=$(realpath "$LABS_DIR")
            # Check if the path is a directory and add '/' if it's missing
            if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then
                LABS_DIR="$LABS_DIR/"
            fi
        fi
        NUMBER=$3
        echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}"
        echo -e "${BLUE}Lab number:${NO_COLOR} ${YELLOW}$NUMBER${NO_COLOR}"
    else # Use default path for PNETLab Store
        LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/"
        NUMBER=$1
    fi

    if ! [[ -d $LABS_DIR ]]; then
        echo "\"Your labs from PNETLab Store\" directory has not been found"
        echo "This folder is automatically created when a lab from PNETLab Store is downloaded"
        exit 0
    fi

    if [[ -z "$(ls -A "$LABS_DIR")" ]]; then
        echo "No labs were found at PNETLab Store folder in server"
        exit 0
    fi

    declare -a readableLabsArray=()
    for file in "$LABS_DIR"*; do
        check=$(grep -Pio 'encoding="UTF-8"' "$file")
        SUB="encoding"
        if [[ $check == *"$SUB"* ]]; then
            readableLabsArray+=("$file")
        fi
    done

    counter_for_readable_labs=0
    for f in "${readableLabsArray[@]}"; do
        counter_for_readable_labs=$((counter_for_readable_labs + 1))
    done

    if [[ ${#readableLabsArray[@]} -eq 0 ]]; then
        STR="No readable labs were found so we cannot download images for any lab"
        echo -e "${RED}[-]$STR\033[0m"
        exit 0
    fi

    if [[ $NUMBER -gt $counter_for_readable_labs || $NUMBER -le 0 ]]; then
        if [[ $counter_for_readable_labs -eq 1 ]]; then
            STR="Last parameter must be 1 because you have only one lab"
            echo -e "${YELLOW}$STR${NO_COLOR}"
        else
            STR="Last parameter must be a number between 1 and $counter_for_readable_labs"
            echo -e "${YELLOW}$STR${NO_COLOR}"
        fi

        echo -e
        list_labs "$1" "$LABS_DIR"
        exit 0
    fi

    file=${readableLabsArray[$((NUMBER - 1))]}
    echo -e "File selected: $file"
    TYPES=(iol dynamips qemu docker)
    for type in "${TYPES[@]}"; do
        images=$(grep -Pio "type=\"$type\".*image=\"\K[^\"]*" "$file" | sort -u)
        echo "$images" >/opt/unetlab/labs/"$type"_images.txt
    done

    list_iol_images=/opt/unetlab/labs/iol_images.txt
    list_dynamips_images=/opt/unetlab/labs/dynamips_images.txt
    list_qemu_images=/opt/unetlab/labs/qemu_images.txt
    list_docker_images=/opt/unetlab/labs/docker_images.txt

    BIN_FLAG=0
    if ! grep -q '[^[:space:]]' $list_iol_images; then BIN_FLAG=1; fi

    DYNAMIPS_FLAG=0
    if ! grep -q '[^[:space:]]' $list_dynamips_images; then DYNAMIPS_FLAG=1; fi

    QEMU_FLAG=0
    if ! grep -q '[^[:space:]]' $list_qemu_images; then QEMU_FLAG=1; fi

    DOCKER_FLAG=0
    if ! grep -q '[^[:space:]]' $list_docker_images; then DOCKER_FLAG=1; fi

    print_separator "List of images found on lab"
    for type in "${TYPES[@]}"; do
        print_separator "List of ${type^^} images"
        list_file="/opt/unetlab/labs/${type}_images.txt"
        if ! grep -qv -e '^\s*$' "$list_file"; then
            echo "No ${type} images found"
        else
            n=1
            while read -r line; do
                echo "File $n : $line"
                n=$((n + 1))
            done <"$list_file"
        fi
    done

    TYPES=(iol dynamips qemu)
    for type in "${TYPES[@]}"; do
        print_separator "Start downloading ${type^^} images for this lab"
        pull_lab_images "$file" "$type"
    done

    print_separator "Start downloading DOCKER images for this lab"
    echo -e "\033[32m$STR_DOCKER\033[0m"
    if [[ DOCKER_FLAG -eq 1 ]]; then
        echo -e "${YELLOW}- Nothing to download${NO_COLOR}"
    else
        echo "Docker images"
        download_lab_docker_images
    fi

    # Remove csv file
    rm "$(pwd)"/"$FILENAME" >/dev/null 2>&1

    # Remove files
    rm /opt/unetlab/labs/{iol_images.txt,dynamips_images.txt,qemu_images.txt,docker_images.txt} >/dev/null 2>&1
}

# Function to download images for all labs
install_images_all_labs() {
    # Check if the user passed a custom path for Labs directory
    if [[ $1 == "mylabs" ]]; then # If the command is "ishare2 mylabs"
        LABS_DIR=$2
        CUSTOM_PATH_FLAG=1
        if [[ ! "$LABS_DIR" == /* ]]; then # If the path is not absolute
            # Convert the path to absolute
            LABS_DIR=$(realpath "$LABS_DIR")
            # Check if the path is a directory and add '/' if it's missing
            if [[ -d "$LABS_DIR" && "$LABS_DIR" != */ ]]; then
                LABS_DIR="$LABS_DIR/"
            fi
        fi
        NOT_FOUND_MESSAGE="Make sure you are passing the correct path to the folder where your labs are located"
        echo -e "${BLUE}Custom path for Labs directory:${NO_COLOR} ${YELLOW}$LABS_DIR${NO_COLOR}"
    else # Use default path for PNETLab Store
        LABS_DIR="/opt/unetlab/labs/Your labs from PNETLab Store/"
        NOT_FOUND_MESSAGE="This folder is automatically created when a lab from PNETLab Store is downloaded"
    fi

    if ! [[ -d $LABS_DIR ]]; then
        echo "\"$LABS_DIR\" directory has not been found"
        echo "$NOT_FOUND_MESSAGE"
        exit 0
    fi

    if [[ -z "$(ls -A "$LABS_DIR")" ]]; then
        echo "No labs found in \"$LABS_DIR\" directory"
        exit 0
    fi

    declare -a readableLabsArray=()
    for file in "$LABS_DIR"*; do
        check=$(grep -Pio 'encoding="UTF-8"' "$file")
        SUB="encoding"
        if [[ $check == *"$SUB"* ]]; then
            readableLabsArray+=("$file")
        fi
    done

    N=${#readableLabsArray[@]}

    if [[ $N -eq 0 ]]; then
        STR="No readable labs have been found so there is no way to continue with this command"
        echo -e "${RED}[-]$STR\033[0m"
        exit 0
    fi

    if [[ $N -eq 1 ]]; then
        echo "Starting to download images for $N lab"
    else
        echo "Starting to download images for $N labs"
    fi

    for ((i = 1; i <= N; i++)); do
        echo -e "\n\033[32mLab $i/$N\033[0m"
        if [[ $CUSTOM_PATH_FLAG -eq 1 ]]; then
            install_images_from_lab "mylabs" "$LABS_DIR" "$i"
        else
            install_images_from_lab "$i"
        fi
    done
    echo -e "\nDone"
}

# Function to download all images of a specific type (IOL, DYNAMIPS, QEMU, DOCKER)
pull_all() {
    TYPE=${1^^}
    if [[ $TYPE == "BIN" ]]; then
        TYPE="IOL"
    fi
    if [[ $TYPE == "QEMU" ]]; then
        echo -e "${YELLOW}[!] WARNING: Pulling all QEMU images will take a long time.\n [!] Make sure you have enough disk space and bandwidth!${NO_COLOR}"
        # Enter confirmation text
        CONFIRMATION="I understand that pulling all QEMU images will take a long time. I have enough disk space and bandwidth."
        # Ask the user to enter the confirmation text
        read -p "[?] Please enter the following text to confirm: \"$CONFIRMATION\" (case sensitive): " -r
        # If the confirmation text is incorrect then exit
        if [[ ! $REPLY =~ ^$CONFIRMATION$ ]]; then
            echo -e "${RED}[!] Aborting.${NO_COLOR}"
            exit 1
        fi
    fi
    echo -e "${YELLOW}[+] Pulling all $TYPE images...${NO_COLOR}"
    # Ask for conirmation
    read -p "[?] Are you sure you want to pull all $TYPE images? (y/n): " -r
    # If reply was no Y or y then exit
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        echo -e "${RED}[!] Aborting.${NO_COLOR}"
        exit 1
    fi
    # Get the list of all images
    fetch_json
    # Get all the IDs for the images of the specified type
    IDS=$(jq -r --arg type "$TYPE" '.[$type][] | .id' "$TEMP_JSON")
    # Pull all the images
    i=1
    total=$(echo "$IDS" | wc -l)
    for ID in $IDS; do
        echo -e "${YELLOW}[+] Pulling image $i/$total...${NO_COLOR}"
        ishare2 pull ${TYPE,,} $ID
        i=$((i + 1))
    done
}

# Function to pull lab images
pull_lab_images() {
    # Pending to implement: corrections_for_bin_images_in_lab_"$IMAGE_NAME" "$LAB_PATH"
    # Pending to implement: corrections_for_qemu_images_in_lab_"$QEMU_NAME"
    LAB_PATH="$1"
    IMAGE_TYPE="$2"
    IMAGE_TYPE_UPPERCASE=$(echo "$IMAGE_TYPE" | tr '[:lower:]' '[:upper:]')

    filename1="/opt/unetlab/labs/${IMAGE_TYPE}_images.txt"
    n=1
    while read -r line; do
        n=$((n + 1))
        IMAGE_NAME="$line"
        if [[ -z $IMAGE_NAME ]]; then
            echo "No $IMAGE_TYPE images found"
        else
            fetch_json
            # Clear the ID variable at the start of each iteration
            ID=""
            # Search for the image in the "IMAGE_TYPE" section of the JSON file and get the ID
            ID=$(jq -r --arg image_name "$IMAGE_NAME" '.["'"$IMAGE_TYPE_UPPERCASE"'"][] | select(.name == $image_name) | .id' "$TEMP_JSON")

            if [ -n "$ID" ]; then
                # The image was found in the "IMAGE_TYPE" section of the JSON data
                echo "Image '$IMAGE_NAME' found in JSON data. ID: $ID"
                # Download the image
                ishare2 pull "$IMAGE_TYPE" "$ID"
            else
                echo "Image '$IMAGE_NAME' not found in JSON data."
            fi
        fi
    done <"$filename1"
}

# Function to check docker service status
check_docker_service_status() {
    local status=$(service docker status)

    if echo "$status" | grep -q "active (running)"; then
        return 0
    fi

    if echo "$status" | grep -q "inactive (dead)"; then
        echo -e "${RED}Detected: Docker service is down. Trying to restart it${NO_COLOR}"
        service docker restart
        status=$(service docker status)
    fi

    if echo "$status" | grep -q "active (running)"; then
        echo -e "${GREEN}Docker service has been restarted successfully${NO_COLOR}"
        return 0
    else
        echo -e "${RED}There was a problem trying to start docker service${NO_COLOR}"
        echo -e "Information about service docker status command:\n"
        echo "$status"
    fi
}

# Function to list docker images
list_dockers() {
    echo -e "\nDocker images in server"
    docker images
}

# Function to count docker images
count_dockers() {
    data=$(docker images | wc -l)
    docker_count=$((data - 1))

    if [[ $docker_count -eq 1 ]]; then
        echo -e "\n$docker_count docker image found in server"
    else
        echo -e "\n$docker_count docker images found in server"
    fi
}

# Function to download lab docker images using the correct docker image name
corrections_for_docker_images_in_lab_function() {
    DOCKER_NAME=$1
    LAB_PATH=$2

    declare -a array=(
        "eve-ostinato"
        "eve-ostinato-bionic"
        "eve-ostinato-focal"
        "eve-dind"
        "eve-dind-focal"
        "eve-chrome"
        "eve-chrome-bionic"
        "eve-chrome-focal"
        "eve-firefox"
        "eve-firefox-bionic"
        "eve-firefox-focal"
        "eve-desktop"
        "eve-desktop-bionic"
        "eve-desktop-focal"
        "eve-gui-server"
        "eve-gui-server-bionic"
        "eve-gui-server-focal"
        "eve-wireshark"
        "eve-wireshark-bionic"
        "eve-wireshark-focal"
        "eve-kali"
        "eve-kali-large"
        "eve-napalm-focal"
        "eve-ansible-focal"
        "dockergui-rdp"
        "eve-gui-server:latest"
        "eve-gui-server-bionic:latest"
    )

    for i in "${array[@]}"; do
        if [[ $DOCKER_NAME == "$i" ]]; then
            OLD_FILENAME=$DOCKER_NAME
            NEW_FILENAME=eveng/$DOCKER_NAME

            DOCKER_NAME=$NEW_FILENAME
            echo -e "\nDocker image name changed from:\n$OLD_FILENAME"
            echo -e "to\n$NEW_FILENAME\n"

            echo "Changing docker image name inside .unl lab file"
            echo Lab file: "$LAB_PATH"
            sed -i -e s+"$OLD_FILENAME"+"$NEW_FILENAME"+g "$LAB_PATH"
            echo -e "Changing: OK\n"
        fi
    done
}

# Function to download lab docker images
download_lab_docker_images() {
    filename1=/opt/unetlab/labs/docker_images.txt

    # Checking docker service status before pull any docker image
    if check_docker_service_status; then # When 0 is returned
        while read -r line; do
            DOCKER_NAME=$line

            corrections_for_docker_images_in_lab_"$DOCKER_NAME" "$LAB_PATH"

            STR="Docker requested: "
            echo -e "\033[33m$STR\033[0m $DOCKER_NAME"

            docker pull "$DOCKER_NAME"
        done <"$filename1"
    fi

    list_dockers
    count_dockers
}

# Function to get the version of ishare2
ishare2_version() {
    # Check if file is empty or not exists or different from installed $VERSION
    echo $VERSION >$ISHARE2_DIR/ishare2_version
    # Set CHANNEL on ishare2.conf
    sed -i "s/CHANNEL=.*/CHANNEL=$CHANNEL/" $ISHARE2_CONF_FILE
    if [[ $1 == "show" ]]; then
        echo -e "ishare2 version: $(cat $ISHARE2_DIR/ishare2_version)"
        echo -e "Latest version: $(curl -s --insecure "$URL_VERSION")"
    fi
}

# Function to upgrade ishare2
upgrade_ishare2() {
    LOCAL_VALUE=$(cat $ISHARE2_DIR/ishare2_version)
    UPSTREAM_VERSION=$(curl -s --insecure "$URL_VERSION")

    if [[ $LOCAL_VALUE == "$UPSTREAM_VERSION" ]]; then
        echo -e "${YELLOW}[+] ishare2 $UPSTREAM_VERSION is already in the newest version available. ${NO_COLOR}"
        echo -e "${YELLOW}[+] Do you want to reinstall it? (y/n) ${NO_COLOR}"
        read -r answer
        if [[ $answer == "y" || $answer == "Y" ]]; then
            echo -e "${YELLOW}[+] Reinstalling ishare2... ${NO_COLOR}"
        else
            exit 0
        fi
    fi

    rm /usr/sbin/ishare2
    download_file_wget "$URL_ISHARE2" /usr/sbin/ "ishare2"

    echo -e "${GREEN}[+] Successfully downloaded the latest version of ishare2!${NO_COLOR}"
    # Make ishare2 executable
    echo -e "${YELLOW}[+] Setting ishare2 permissions${NO_COLOR}"
    chmod +x /usr/sbin/ishare2
    # Show success message
    echo -e "${GREEN}[+] ishare2 was upgraded from $LOCAL_VALUE to $UPSTREAM_VERSION ${NO_COLOR}"
}

# Function to show the ishare2 upgrade menu
menu_ishare2_upgrade_pnetlab() {
    # Define the menu options
    options=(
        "Upgrade PNETLab v5 STABLE"
        "Upgrade PNETLab v5 BETA"
        "Upgrade PNETLab v6 BETA"
    )

    # Set the prompt for the select menu
    PS3="Please, select an option: "

    # Print the menu and wait for user input
    select opt in "${options[@]}" "Exit"; do
        case "$REPLY" in
        1)
            # Upgrade PNETLab v5
            bash -c "$(curl -sL --insecure "$URL_PNETLAB_V5")"
            ;;
        2)
            # Upgrade PNETLab v5 Beta
            bash -c "$(curl -sL --insecure "$URL_PNETLAB_V5_BETA")"
            ;;
        3)
            # Upgrade PNETLab v6
            bash -c "$(curl -sL --insecure "$URL_PNETLAB_V6")"
            ;;
        $((${#options[@]} + 1)))
            break
            ;;
        *)
            # Invalid option
            total_options=${#options[@]}
            echo -e "${RED}Invalid option: Select a number from 1 to $total_options${NO_COLOR}"
            continue
            ;;
        esac
    done
}

# Function to show the ishare2 upgrade menu
menu_ishare2_upgrade() {
    prompt="Please, select an option: "
    options=(
        "Upgrade ishare2"
        "Upgrade PNETLab"
        "Upgrade ishare2 GUI"
    )

    PS3="$prompt"
    select opt in "${options[@]}" "Exit"; do
        case "$REPLY" in
        1)
            upgrade_ishare2
            break
            ;;
        2)
            menu_ishare2_upgrade_pnetlab
            break
            ;;
        3)
            ishare2 gui install
            break
            ;;
        $((${#options[@]} + 1)))
            break
            ;;
        *)
            STR="Invalid option: Select a number from 1 to 4"
            echo -e "${RED}$STR${NO_COLOR}"
            continue
            ;;
        esac
    done
}

# Function to show the ishare2 changelog
show_changelog() {
    FILE=/root/CHANGELOG.md
    if [[ -e $FILE ]]; then rm $FILE; fi

    wget -q "$URL_CHANGELOG_MD" -P /root
    head -n 15 $FILE
    rm $FILE
}

# Function to install ishare2 GUI
ishare2_gui_install() {
    curl -sL "$URL_ISHARE2_GUI_INSTALL" | bash
}

# Function to start ishare2 GUI
ishare2_gui_start() {
    systemctl start ishare2_gui.service
    if [[ $? -ne 0 ]]; then
        echo -e "${RED}[-] Error starting ishare2 GUI service. ${NO_COLOR}"
        echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}"
        exit 1
    fi
}

# Function to stop ishare2 GUI
ishare2_gui_stop() {
    systemctl stop ishare2_gui.service
    if [[ $? -ne 0 ]]; then
        echo -e "${RED}[-] Error stopping ishare2 GUI service. ${NO_COLOR}"
        echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}"
        exit 1
    fi
}

# Function to restart ishare2 GUI
ishare2_gui_restart() {
    systemctl restart ishare2_gui.service
    if [[ $? -ne 0 ]]; then
        echo -e "${RED}[-] Error restarting ishare2 GUI service. ${NO_COLOR}"
        echo -e "${RED}[-] Please see the logs for troubleshooting. Run: systemctl status ishare2_gui.service ${NO_COLOR}"
        exit 1
    fi
}

# Function to search for images in the JSON index
search_images() {
    TYPE=${1^^}
    FILTER=${2:-}
    VALID_TYPES=("IOL" "QEMU" "DYNAMIPS")

    if [[ "$TYPE" == "ANY" || ! " ${VALID_TYPES[*]} " =~ " ${TYPE} " ]]; then
        SEARCH_ACROSS=true
    else
        SEARCH_ACROSS=false
    fi

    fetch_json

    if [[ "$SEARCH_ACROSS" == true ]]; then
        print_separator "Searching across all types for \"$FILTER\""

        data=$(jq -r --arg filter "$FILTER" '
          . as $root
          | ["IOL", "QEMU", "DYNAMIPS"] 
          | map($root[.][] 
            | select(($filter | length == 0) or (.name | test($filter; "i")))
            | [.id, .name, ., .metadata.total_human_size] 
            | [.[0], .[1], .[2].type, .[3]]) 
          | .[] 
          | @tsv
        ' "$TEMP_JSON")

        if [[ -z "$data" ]]; then
            echo -e "\n${RED}No matching images found for \"$FILTER\"${NO_COLOR}"
        else
            {
                echo -e "ID\tNAME\tTYPE\tSIZE"
                echo -e "--\t----\t----\t----"
                echo "$data"
            } | column -t -s $'\t'

            total_count=$(echo "$data" | wc -l)
            echo -e "\n${GREEN}$total_count image(s) found${NO_COLOR}"
        fi

    else
        print_separator "Available $TYPE images"

        data=$(jq -r --arg type "$TYPE" --arg filter "$FILTER" '
          .[$type][] 
          | select(($filter | length == 0) or (.name | test($filter; "i"))) 
          | [.id, .name, .metadata.total_human_size] 
          | @tsv' "$TEMP_JSON")

        if [[ -z "$data" ]]; then
            echo -e "\n${RED}No $TYPE images found for \"$FILTER\"${NO_COLOR}"
        else
            {
                echo -e "ID\tNAME\tSIZE"
                echo -e "--\t----\t----"
                echo "$data"
            } | column -t -s $'\t'

            total_count=$(echo "$data" | wc -l)
            echo -e "\n${GREEN}$total_count $TYPE image(s) found${NO_COLOR}"
        fi
    fi
    echo -e "${YELLOW}[!] ${NO_COLOR}You can use the command \033[32mishare2 pull <type> <id>\033[0m to download the image"
}

# Function to update iptables and add them to startup script
block_cisco() {
    # Remove entries containing "cisco xml" from /etc/hosts
    sed -i '/cisco xml/d' /etc/hosts || {
        echo -e "${RED}[-] Failed to remove entries containing 'cisco xml' from /etc/hosts${NO_COLOR}"
        logger error "Failed to remove entries containing 'cisco xml' from /etc/hosts"
        exit 1
    }

    # Add the iptables rule to block cisco xml requests to ovfstartup
    echo 'iptables -I OUTPUT -p udp --dport 53 -m string --hex-string "|03|xml|05|cisco|03|com" --algo bm -j DROP' >>/opt/ovf/ovfstartup || {
        echo -e "${RED}[-] Failed to add iptables rule to block cisco xml requests${NO_COLOR}"
        logger "error Failed to add iptables rule to block cisco xml requests"
        exit 1
    }

    # Restart the ovfstartup service
    service ovfstartup restart || {
        echo -e "${RED}[-] Failed to restart ovfstartup service${NO_COLOR}"
        logger error "Failed to restart ovfstartup service"
        exit 1
    }

    # If no errors occurred, show success message
    echo "${GREEN}[+] Successfully updated iptables and added them to the ovfstartup script${NO_COLOR}"
}

# Function to generate a new iourc license
generate_iourc_license() {
    BIN_PATH="/opt/unetlab/addons/iol/bin/"

    PYTHON_FILE=$BIN_PATH"CiscoIOUKeygen.py"
    PERL_FILE=$BIN_PATH"keepalive.pl"

    # Check if python2 is installed
    if ! command -v python &>/dev/null; then
        echo -e "${RED}[-] Python is not installed${NO_COLOR}"
        logger error "Python is not installed"
        # Ask the user if they want to install python
        read -p "[?] Do you want to install python? (y/n): " -r
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            install_package python
        else
            echo -e "${RED}[-] Python2 is required to generate the iourc license${NO_COLOR}"
            exit 1
        fi
    fi

    # Block cisco xml requests
    block_cisco || {
        echo "${RED}[-] Failed to block cisco xml requests${NO_COLOR}"
        logger error "Failed to block cisco xml requests"
        exit 1
    }

    # Check if the CiscoIOUKeygen.py file exists in the bin directory
    if ! [[ -e $PYTHON_FILE ]]; then
        wget -O $PYTHON_FILE "$URL_CISCOIOUKEYGEN_PY" -q || {
            echo -e "${RED}[-] Error downloading CiscoIOUKeygen.py file${NO_COLOR}"
            logger error "Error downloading CiscoIOUKeygen.py file"
            exit 1
        }
    fi

    echo -e "${YELLOW}[!] Executing CiscoIOUKeygen.py file... ${NO_COLOR}"
    python $PYTHON_FILE
    if [[ $? -ne 0 ]]; then
        echo -e "${RED}[-] Error running CiscoIOUKeygen.py file. ${NO_COLOR}"
        echo -e "${RED}[-] Please check your internet connection and try again. ${NO_COLOR}"
        exit 1
    fi

    if ! [[ -e $PERL_FILE ]]; then
        wget -O $PERL_FILE "$URL_KEEPALIVE_PL" -q
        if [[ $? -ne 0 ]]; then
            echo -e "${RED}[-] Error downloading keepalive.pl file. ${NO_COLOR}"
            echo -e "${RED}[-] Please check your internet connection and try again. ${NO_COLOR}"
            exit 1
        fi
    fi
    echo -e "${GREEN}[+] IOL license was generated successfully. ${NO_COLOR}"
}

# Function to show information about ishare2
show_info() {
    echo -e "- For documentation about ishare2, visit: https://github.com/ishare2-org/ishare2-cli"
    echo -e "- If you face any issues with ishare2, please report them at @NetLabHub Telegram Group. Attach screenshots and relevant information."
    echo -e "- You can use ${YELLOW}ishare2 config${NO_COLOR} to change the chosen downloader."
    echo -e "- If you cancel a download, use the flag: --overwrite to first delete the existing image and then download it again."
    echo -e "- To submit new images, please use the following Google Form: https://forms.gle/SotrznrN3CmZdg8M8"
    echo -e "- Telegram Channel: https://t.me/NetLabHub (For announcements and updates)"
    echo -e "- Email: contact@labhub.eu.org"
}

# Function to show help for ishare2
show_ishare2_help() {
    # For ishare2
    ISHARE2_VERSION_INFO=$(cat $ISHARE2_DIR/ishare2_version)
    UPSTREAM_VERSION=$(curl -s "$URL_VERSION")
    if ! [[ $ISHARE2_VERSION_INFO == "$UPSTREAM_VERSION" ]]; then
        ISHARE2_VERSION_INFO="$ISHARE2_VERSION_INFO $(echo -e "${YELLOW}Update available: $UPSTREAM_VERSION. Run: ishare2 upgrade${NO_COLOR}")"
    else
        ISHARE2_VERSION_INFO="$ISHARE2_VERSION_INFO $(echo -e "${GREEN}Up to date${NO_COLOR}")"
    fi

    # For PNETLab
    data=$(mysql -uroot -ppnetlab -D pnetlab_db -e "SELECT control_value FROM control WHERE control_value>1;" 2>/dev/null)
    pnetlab_info=($data)
    if [[ ${#pnetlab_info[@]} -eq 0 ]]; then
        PNETLAB_INSTALLED=$(echo -e "${RED}PNETLab is not installed.${NO_COLOR}")
    else
        PNETLAB_INSTALLED=$(echo -e "${GREEN}PNETLab v${pnetlab_info[1]} is installed.${NO_COLOR}")
    fi
    cat <<EOF
Syntax

ishare2 [action] [param1] [param2] [--overwrite]

action:
    search      : Search for images by type
    pull        : Download an image by type and number
    installed   : Show installed images on server
    labs        : Show labs on server and download images for those labs
    mylabs      : Same as labs command but using a customized path to labs
    relicense   : Generate a new iourc license for bin images
    upgrade     : Retrieves a menu that allows users to upgrade ishare2 and PNETLab VM
    changelog   : Show the latest changes made to ishare2
    gui         : Web app to use ishare2 in browser
    test        : Test internet connectivity to required sites (Github, Google Sheets)
    help        : Show useful information

param1:
    type = all, bin, qemu, dynamips, docker or name

param2:
    number = This number can be obtained using ishare2 search <type>

--overwrite:
    Used to overwrite an existing image if it already exists on your system.

Examples:

- ishare2 search <type>
    - ishare2 search all
    - ishare2 search bin
    - ishare2 search qemu
    - ishare2 search dynamips
    - ishare2 search <name>
        Examples:
            - ishare2 search vios
            - ishare2 search win-
            - ishare2 search winserver
            - ishare2 search kali
            - ishare2 search mikro
            - ishare2 search forti
            - ishare2 search nxos
            - ishare2 search vmx
            - ishare2 search esxi
            - ishare2 search palo
            - ishare2 search Licensed
            - More <name> options using ishare2 search all

- ishare2 pull bin <number> [--overwrite]
- ishare2 pull qemu <number> [--overwrite]
- ishare2 pull dynamips <number> [--overwrite]

- ishare2 pull bin all
- ishare2 pull qemu all (Not available for qemu type due to its large size)
- ishare2 pull dynamips all

- ishare2 installed all
- ishare2 installed bin
- ishare2 installed qemu
- ishare2 installed dynamips
- ishare2 installed docker

- ishare2 labs
- ishare2 labs <number>
- ishare2 labs all

- ishare2 mylabs <path>
- ishare2 mylabs <path> <number>
- ishare2 mylabs <path> all

- ishare2 gui install
- ishare2 gui start
- ishare2 gui stop
- ishare2 gui restart

- ishare2 relicense
- ishare2 upgrade
- ishare2 changelog
- ishare2 test
- ishare2 config
- ishare2 help

ishare2: $ISHARE2_VERSION_INFO
ishare2 Channel: $CHANNEL
$PNETLAB_INSTALLED
EOF
}

# Function to show quick help for ishare2
ishare2_quick_help() {
    cat <<EOF
Syntax

ishare2 [action] [param1] [param2] [--overwrite]

action:
    search      : Search for images available on LabHub mirrors.
    pull        : Download an image by specifying the type and id.
    installed   : Shows images installed on the server.
    labs        : Shows available labs and downloads the images for the selected lab.
    mylabs      : Same as labs command but you can use a customized path.
    relicense   : Generates an iourc license for iol images.
    upgrade     : Shows a menu to upgrade ishare2 or PNETLab.
    test        : Test internet connectivity to required sites.
    help        : Shows full help information.

param1:
    type = all, iol, qemu, dynamips, docker or name.

param2:
    id = This can be obtained using ishare2 search <type>

--overwrite:
    Used to overwrite an existing image if it already exists on your system.

Try: ishare2 help for more information.
EOF
}

# Function to handle tgz archives
handle_tgz_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".tgz"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        tar -xf "$file" --strip-components 1 -C "$DIR"
        rm "$file"
        echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}"
    fi
}

# Function to handle tar.gz archives
handle_tar_gz_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".tar.gz"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        tar -xf "$file" --strip-components 1 -C "$DIR"
        rm "$file"
        echo -e "${GREEN}[+] Extracted: $DIR."
    fi
}

# Function to handle tar files
handle_tar_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".tar"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        tar -xf "$file" --strip-components 1 -C "$DIR"
        rm "$file"
        echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}"
    fi
}

# Function to handle zip files
handle_zip_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".zip"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        unzip -a -j "$file" -d "$DIR"
        rm "$file"
        echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}"
    fi
}

# Function to handle rar files
handle_rar_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".rar"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")

        if ! [[ -e /usr/bin/unrar ]]; then
            apt -qq update >/dev/null 2>&1 && apt -qq install --assume-yes unrar >/dev/null 2>&1
        fi

        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        unrar e "$file" "$DIR" >/dev/null 2>&1
        echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}"
        rm "$file"
    fi
}

# Function to handle OVA files
handle_ova_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".ova"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        COMPRESSED_FILE=$(ls "${DIR}"/*"$EXTENSION")
        echo -e "${YELLOW}[-] Extracting: $FOLDERNAME$EXTENSION file... ${NO_COLOR}"
        tar -xvf "$COMPRESSED_FILE" -C "$DIR"
        rm "$COMPRESSED_FILE"
        echo -e "${GREEN}[+] Extracted: $DIR. Image ready to use. ${NO_COLOR}"
    fi
}

# Function to handle ISO files
handle_iso_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".iso"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        ISO_FILE=$(ls "${DIR}"/*"$EXTENSION")

        if ! [[ "${ISO_FILE: -9}" == "cdrom.iso" ]]; then
            mv "$ISO_FILE" "$DIR"/cdrom.iso # Rename file
            echo -e "${GREEN}[+] ISO file saved as: $DIR/cdrom.iso ${NO_COLOR}"
        fi

        if ! [[ -e /usr/bin/qemu-img ]]; then
            echo -e "${YELLOW}[!] qemu-img is not installed. Attempting to install it... ${NO_COLOR}"
            install_packages "qemu-utils"
        fi

        if [[ $FILES_COUNTER -eq 1 ]]; then
            qemu-img create -f qcow2 "$DIR"/virtioa.qcow2 20G
        fi
    fi
}

# Function to handle yaml templates
handle_yml_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSIONS=(".yml" ".yaml")
    #YML_DIR="/opt/unetlab/html/templates/" # Value passed from set_yml_template_folder_location()

    for ext in "${EXTENSIONS[@]}"; do
        if ls "${DIR}"/*"$ext" &>/dev/null; then
            file=$(ls "${DIR}"/*"$ext" | head -1)
            cp "$file" "$YML_DIR"
            break
        fi
    done
}

# Function to handle txt and md files
handle_txt_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSIONS=(".txt" ".md")

    for ELEMENT in "$DIR"/*; do
        for EXTENSION in "${EXTENSIONS[@]}"; do
            if [[ "${ELEMENT: -${#EXTENSION}}" == "$EXTENSION" ]]; then
                echo -e "\nReading file: $ELEMENT\n"
                cat "$ELEMENT"
                echo -e
                break
            fi
        done
    done
}

# Function to handle sh files
handle_sh_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".sh"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        chmod +x "$file"
        echo -e "This image includes a script file: $file"
        read -p "Do you want to run $file? [y/n]: " -n 1 -r
        echo ""
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            bash "$file"
        else
            echo -e "${YELLOW}[!] $file was not executed. ${NO_COLOR}"
            echo -e "${YELLOW}[!] WARN: This script file might be needed for the image to work properly. ${NO_COLOR}"
        fi
    fi
}

# Function to handle png files (icons)
handle_png_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".png"
    PNG_DIR="/opt/unetlab/html/images/icons/"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")

        # Change .png filename for a Huawei device: from ne5000e.png to ne.png
        if [[ $file == "/opt/unetlab/addons/qemu/huaweine5ke-ne5000e/ne5000e.png" ]]; then
            NEW_PNG_FILENAME="/opt/unetlab/addons/qemu/huaweine5ke-ne5000e/ne.png"
            mv "$file" $NEW_PNG_FILENAME
            file=$NEW_PNG_FILENAME
        fi

        cp "$file" $PNG_DIR
    fi
}

# Function to handle php files
handle_php_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".php"
    PHP_DIR="/opt/unetlab/html/devices/qemu/"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        file=$(ls "${DIR}"/*"$EXTENSION")
        cp "$file" $PHP_DIR
    fi
}

# Function to handle vmdk files
handle_vmdk_files() {
    FOLDERNAME=$1

    DIR="/opt/unetlab/addons/qemu/$FOLDERNAME"

    EXTENSION=".vmdk"

    if ls "${DIR}"/*"$EXTENSION" &>/dev/null; then
        VMDK_FILE=$(ls "${DIR}"/*"$EXTENSION")
        install_qemu_tools

        echo -e "\nConverting .vmdk to .qcow2. It might take a while..."
        /opt/qemu/bin/qemu-img convert -p -f vmdk -O qcow2 "$VMDK_FILE" "$DIR"/hda.qcow2
        echo "File converted successfully"
        rm "$VMDK_FILE"
    fi
}

# Function to install qemu-utils
install_qemu_tools() {
    apt update
    apt install -y qemu-utils
    # check if qemu-img is installed
    if ! [[ -e /opt/qemu/bin/qemu-img ]]; then
        echo -e "${RED}[-] qemu-img is not installed. Please install it manually. ${NO_COLOR}"
    fi
}

# Function to pull images passed as parameters
pull_images() {
    local IMAGE_TYPE=$1
    local IMAGE_ID=$2
    local OVERWRITE=$3

    # Validate parameters
    [[ -z "$IMAGE_TYPE" ]] && {
        echo "Usage: $0 pull <type> <id>"
        exit 1
    }
    [[ -z "$IMAGE_ID" || ! "$IMAGE_ID" =~ ^[0-9]+$ ]] && {
        echo "Invalid ID"
        exit 1
    }

    IMAGE_TYPE=${IMAGE_TYPE^^}
    [[ "$IMAGE_TYPE" == "BIN" ]] && IMAGE_TYPE="IOL"
    [[ ! "$IMAGE_TYPE" =~ ^(QEMU|IOL|DYNAMIPS)$ ]] && {
        echo "Invalid type"
        exit 1
    }

    logger debug "Image type: $IMAGE_TYPE"
    logger debug "Image ID: $IMAGE_ID"

    fetch_json || {
        echo "Failed to fetch index"
        exit 1
    }

    local image_data image_name mirror install_path protocol hostname prefix download_path

    image_data=$(jq -r --arg type "$IMAGE_TYPE" --argjson id "$IMAGE_ID" \
        '.[$type][] | select(.id == $id)' "$TEMP_JSON")
    [[ -z "$image_data" ]] && {
        echo "Image not found"
        exit 1
    }

    image_name=$(jq -r '.name' <<<"$image_data")
    install_path=$(jq -r '.metadata.install_path' <<<"$image_data")
    protocol=$(jq -r '.url_properties.protocol' "$TEMP_JSON")

    # Mirror selection logic
    local state_file="$ISHARE2_DIR/last_mirror"
    local available_mirrors=("drive" "main")

    if [[ "$ROTATE" == "true" ]]; then
        # Read last used mirror or default to first available
        if [[ -f "$state_file" ]]; then
            last_mirror=$(<"$state_file")
            # Find next mirror in rotation
            for i in "${!available_mirrors[@]}"; do
                if [[ "${available_mirrors[$i]}" == "$last_mirror" ]]; then
                    next_index=$(((i + 1) % ${#available_mirrors[@]}))
                    mirror="${available_mirrors[$next_index]}"
                    break
                fi
            done
        else
            mirror="${available_mirrors[0]}"
        fi
        # Save selected mirror for next rotation
        mkdir -p "$(dirname "$state_file")"
        echo "$mirror" >"$state_file"
    else
        # Use MIRROR environment variable if set and valid
        if [[ -n "$MIRROR" ]]; then
            # Check if MIRROR value is valid
            if [[ " ${available_mirrors[*]} " == *" $MIRROR "* ]]; then
                mirror="$MIRROR"
            else
                mirror="main" # Default to main if invalid
                logger warn "Invalid MIRROR value '$MIRROR'. Defaulting to 'main'"
            fi
        else
            mirror="main" # Default to main if MIRROR not set
        fi
    fi

    hostname=$(jq -r --arg mirror "$mirror" '.url_properties.hostnames[$mirror]' "$TEMP_JSON")
    prefix=$(jq -r --arg mirror "$mirror" '.url_properties.prefixes[$mirror]' "$TEMP_JSON")
    download_path="$install_path"

    logger debug "Using mirror: $mirror"
    logger debug "image_name: $image_name"
    logger debug "install_path (raw): $install_path"
    logger debug "URL base: $protocol://$hostname$prefix"
    logger debug "download_path: $download_path"

    # Check if file exists
    if [ -e "$download_path/$image_name" ] && [ "$OVERWRITE" != "--overwrite" ]; then
        echo "File exists. Use --overwrite"
        exit 0
    fi

    # Build download URLs
    download_urls=()
    while IFS= read -r url; do
        full_url="$protocol://$hostname$prefix$url"
        download_urls+=("$full_url")
    done < <(jq -r '.files[].path' <<<"$image_data")

    echo -e "${YELLOW}[!] IMAGE INFO ${NO_COLOR}"
    printf "%-20s: %s\n" "Name" "$image_name"
    printf "%-20s: %s\n" "Size" "$(jq -r '.metadata.total_human_size' <<<"$image_data")"
    printf "%-20s: %s\n" "Type" "$IMAGE_TYPE"
    printf "%-20s: %s\n" "Path" "$install_path"
    printf "%-20s: %s\n" "Host" "$hostname"
    printf "%-20s: %s\n" "Mirror" "$mirror"

    logger debug "mkdir -p $install_path"
    if [ -e "$install_path" ]; then
        if [ ! -d "$install_path" ]; then
            echo -e "${RED}[-] Error: $install_path exists but is not a directory.${NO_COLOR}"
            logger error "$install_path exists but is not a directory"
            exit 1
        else
            logger debug "Directory already exists, continuing."
        fi
    else
        mkdir -p "$install_path"
    fi

    for url in "${download_urls[@]}"; do
        filename=$(basename "$url")

        logger debug "Remote URL: $url"
        logger debug "filename resolved: $filename"
        logger debug "download_file $url -> $install_path/$filename"

        download_file "$url" "$install_path" "$filename"

        expected_size=$(jq -r --arg name "$filename" '.files[] | select(.filename == $name) | .size' <<<"$image_data")
        expected_sha1=$(jq -r --arg name "$filename" '.files[] | select(.filename == $name) | .checksum.sha1' <<<"$image_data")
        expected_md5=$(jq -r --arg name "$filename" '.files[] | select(.filename == $name) | .checksum.md5' <<<"$image_data")

        echo -e "${YELLOW}[-] Running integrity checks for $filename... ${NO_COLOR}"
        logger info "${YELLOW}[-] Running integrity checks for $filename... ${NO_COLOR}"
        check_image_size "$install_path" "$filename" "$expected_size"
        if [[ $? -ne 0 ]]; then
            echo -e "${RED}[-] Size check failed for $filename. ${NO_COLOR}"
            echo -e "${RED}[-] Please try downloading the image again. ${NO_COLOR}"
        fi
        check_image_hash "$install_path" "$filename" "$expected_sha1" "sha1"
        if [[ $? -ne 0 ]]; then
            echo -e "${RED}[-] SHA1 check failed for $filename. ${NO_COLOR}"
            echo -e "${RED}[-] Please try downloading the image again. ${NO_COLOR}"
        fi
        check_image_hash "$install_path" "$filename" "$expected_md5" "md5"
        if [[ $? -ne 0 ]]; then
            echo -e "${RED}[-] MD5 check failed for $filename. ${NO_COLOR}"
            echo -e "${RED}[-] Please try downloading the image again. ${NO_COLOR}"
        fi
        echo -e "${GREEN}[✓] Downloaded and verified: $filename!${NO_COLOR}"
    done

    # Post-processing for QEMU
    if [ "$IMAGE_TYPE" = "QEMU" ]; then
        handle_tgz_files "$image_name"
        handle_tar_gz_files "$image_name"
        handle_zip_files "$image_name"
    fi

    fix_permissions

    echo -e "${GREEN}[✓] Installation completed successfully! ${NO_COLOR}"
}

show_logs() {
    # Show logs for ishare2
    echo -e "${YELLOW}[-] Showing logs for ishare2... ${NO_COLOR}"
    tail -n 50 $ISHARE2_DIR/ishare2.log
}

clear_logs() {
    # Clear logs for ishare2
    echo -e "${YELLOW}[-] Clearing logs for ishare2... ${NO_COLOR}"
    true >$ISHARE2_DIR/ishare2.log
    echo -e "${GREEN}[+] Logs cleared successfully. ${NO_COLOR}"
}

# Function to fix permissions of the downloaded images
fix_permissions() {
    UNL_WRAPPER="/opt/unetlab/wrappers/unl_wrapper"
    if ! [[ -e $UNL_WRAPPER ]]; then
        echo -e "${RED}[-] unl_wrapper not found at $UNL_WRAPPER or it is not accessible. ${NO_COLOR}"
        logger error "unl_wrapper not found at $UNL_WRAPPER or it is not accessible"
        logger info "Trying to fix permissions manually"
        if chmod 755 -R /opt/unetlab/addons; then
            echo -e "${GREEN}[✓] Permissions fixed manually. ${NO_COLOR}"
        else
            echo -e "${RED}[-] Failed to fix permissions manually. ${NO_COLOR}"
        fi

        return
    fi
    echo -e "${YELLOW}[-] Fixing permissions... ${NO_COLOR}"
    chmod +x $UNL_WRAPPER
    $UNL_WRAPPER -a fixpermissions

    if [ $? -ne 0 ]; then
        echo -e "\n${RED}[-] Failed to fix permissions ${NO_COLOR}"
        exit 1
    fi

    echo -e "\n${GREEN}[+] Fix permissions command has been executed correctly ${NO_COLOR}"
}

# Function to run Internet connection tests
connection_tests() {
    # Define an associative array with service names and their respective IP addresses or domains
    declare -A SERVICES=(
        ["GitHub"]="github.com"
        ["GitHub Raw Files"]="raw.githubusercontent.com"
        ["LabHub (Onedrive backend)"]="labhub.eu.org"
        ["LabHub (Google Drive backend)"]="drive.labhub.eu.org"
        ["Google DNS"]="8.8.8.8"
    )

    # Initialize a flag to track if all services are reachable
    local all_services_reachable=true

    # Header
    echo -e "${YELLOW}[-] Running connection tests... ${NO_COLOR}"

    # Loop through the services and check their reachability
    for service in "${!SERVICES[@]}"; do
        echo -e "${YELLOW}[-] Checking if $service is reachable... ${NO_COLOR}"
        if ping -q -c 5 -W 5 "${SERVICES[$service]}" >/dev/null; then
            echo -e "${GREEN}[+] $service is reachable. ${NO_COLOR}"
        else
            echo -e "${RED}[-] $service is not reachable. ${NO_COLOR}"
            all_services_reachable=false
        fi
    done

    # If any service is not reachable, attempt to fix DNS
    if [ "$all_services_reachable" = false ]; then
        echo -e "${YELLOW}[?] Some services are not reachable. Do you want to attempt to fix DNS? [y/n] ${NO_COLOR}"
        read -r -n 1 -p "Choice: " choice
        echo
        case $choice in
        y | Y)
            fix_dns
            ;;
        n | N)
            echo -e "${YELLOW}[-] DNS was not fixed. ${NO_COLOR}"
            ;;
        *)
            echo -e "${RED}[-] Invalid choice. DNS was not fixed. ${NO_COLOR}"
            ;;
        esac
    fi

    # Report the overall status
    if [ "$all_services_reachable" = true ]; then
        echo -e "${GREEN}[+] All services are reachable. ${NO_COLOR}"
        return 0
    else
        echo -e "${RED}[-] Some services are not reachable. ${NO_COLOR}"
        return 1
    fi
}

# Function to fix DNS (common issue with resolv.conf)
fix_dns() {
    echo -e "${YELLOW}[-] Attempting to fix DNS... ${NO_COLOR}"
    echo -e "${YELLOW}[-] Running: systemctl restart systemd-resolved.service ${NO_COLOR}"
    systemctl restart systemd-resolved.service
    echo -e "${YELLOW}[-] Attempting to fix DNS by setting nameserver to 8.8.8.8... ${NO_COLOR}"
    echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf >/dev/null
    if [ $? -eq 0 ]; then
        echo -e "${GREEN}[+] DNS fix applied successfully. ${NO_COLOR}"
    else
        echo -e "${RED}[-] Failed to update DNS settings. ${NO_COLOR}"
    fi
}

# Function to handle parameters passed to ishare2
selector() {
    case "$1" in
    "search")
        TYPE_INPUT="${2,,}" # normalize to lowercase
        QUERY="${3:-}"      # optional filter

        case "$TYPE_INPUT" in
        "qemu" | "iol" | "dynamips")
            TYPE="${TYPE_INPUT^^}" # convert to uppercase
            search_images "$TYPE" "$QUERY"
            ;;

        "bin") # alias for iol
            TYPE="IOL"
            search_images "$TYPE" "$QUERY"
            ;;

        "all")
            # If keyword is passed, search across all types
            if [[ -n "$QUERY" ]]; then
                search_images "any" "$QUERY"
            else
                for type in "QEMU" "IOL" "DYNAMIPS"; do
                    search_images "$type"
                    echo
                done
            fi
            ;;

        *)
            if [ -n "$2" ]; then
                # Assume $2 is a filter term, so search across all types
                search_images "any" "$2"
            else
                echo "Usage: $SCR_NAME search <type> [keyword]"
                echo "       $SCR_NAME search all [keyword]"
                echo "Types: qemu, iol, dynamips, bin, all"
                exit 1
            fi
            ;;
        esac
        ;;
    "pull")
        case "$2" in
        "qemu" | "iol" | "dynamips" | "bin")
            TYPE="${2^^}" # Convert to uppercase
            if [[ "$3" ]]; then
                if [[ "$3" = "all" ]]; then
                    pull_all "$TYPE"
                    exit 0
                fi
            fi
            pull_images "$TYPE" "$3" "$4"
            ;;
        *) # If the type is not specified, print the usage
            echo "Usage: $SCR_NAME pull <type> <id>"
            echo "       $SCR_NAME pull <type> all"
            echo "Types: qemu, iol, dynamips"
            ;;
        esac
        ;;
    "installed")
        if [[ "$2" ]]; then
            case "$2" in
            "all")
                ishare2 installed qemu
                ishare2 installed dynamips
                ishare2 installed iol
                ishare2 installed docker
                ;;
            "qemu")
                echo -e
                print_separator "Showing installed ${2^^} images"
                tree -hp /opt/unetlab/addons/qemu/
                echo -e
                ;;
            "dynamips")
                echo -e
                print_separator "Showing installed ${2^^} images"
                tree -hp /opt/unetlab/addons/dynamips/
                echo -e
                ;;
            "bin" | "iol")
                echo -e
                print_separator "Showing installed ${2^^} images"
                tree -hp /opt/unetlab/addons/iol/bin/
                echo -e
                ;;
            "docker")
                list_dockers
                count_dockers
                ;;
            *)
                echo "Usage: $SCR_NAME installed <type>"
                echo "       $SCR_NAME installed all"
                echo "Types: qemu, dynamips, iol, docker"
                ;;
            esac
        else
            echo "Usage: $SCR_NAME installed <type>"
            echo "       $SCR_NAME installed all"
            echo "Types: qemu, dynamips, iol, docker"
        fi
        ;;
    "labs")
        if [[ "$2" ]]; then
            if [[ "$2" = "all" ]]; then
                install_images_all_labs "$1"
                exit 0
            else
                install_images_from_lab "$2"
                exit 0
            fi
        fi
        list_labs "$1"
        ;;
    "mylabs")
        if [[ "$2" ]]; then
            if [[ "$2" = "all" ]]; then
                install_images_all_labs "$1"
                exit 0
            else
                install_images_from_lab "$1" "$2" "$3"
                exit 0
            fi
        fi
        list_labs "$1"
        ;;
    "relicense" | "license")
        generate_iourc_license
        ;;
    "upgrade")
        menu_ishare2_upgrade
        ;;
    "changelog")
        show_changelog
        ;;
    "fix")
        # if $2 is not passed
        if [[ -z "$2" ]]; then
            echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
            echo -e "Usage: $SCR_NAME fix <action>"
            echo -e "Actions: permissions, dns, block_cisco"
            exit 1
        fi
        case "$2" in
        "permissions")
            fix_permissions
            ;;
        "dns")
            fix_dns
            ;;
        "block_cisco")
            block_cisco
            ;;
        *) # If the action is not specified, print the usage
            echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
            echo -e "Usage: $SCR_NAME fix <action>"
            echo -e "Actions: permissions, dns"
            exit 1
            ;;
        esac
        ;;
    "logs")
        if [[ "$2" ]]; then
            case "$2" in
            "show")
                show_logs
                ;;
            "clear")
                clear_logs
                ;;
            *)
                echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
                echo -e "Usage: $SCR_NAME logs <action>"
                echo -e "Actions: show, clear"
                exit 1
                ;;
            esac
        else
            echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
            echo -e "Usage: $SCR_NAME logs <action>"
            echo -e "Actions: show, clear"
            exit 1
        fi
        ;;
    "gui")
        echo -e "${RED}[-] This feature won't work most of the time. ${NO_COLOR}"
        if [[ "$2" ]]; then
            case "$2" in
            "install")
                ishare2_gui_install
                ;;
            "start")
                ishare2_gui_start
                ;;
            "stop")
                ishare2_gui_stop
                ;;
            "restart")
                ishare2_gui_restart
                ;;
            *)
                echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
                echo -e "Usage: $SCR_NAME gui <action>"
                echo -e "Actions: install, start, stop, restart"
                ;;
            esac
        else
            echo -e "${RED}[-] Invalid action: $2 ${NO_COLOR}"
            echo -e "Usage: $SCR_NAME gui <action>"
            echo -e "Actions: install, start, stop, restart"
        fi
        ;;
    "help")
        show_ishare2_help
        ;;
    "test")
        connection_tests
        ;;
    "config")
        create_config
        ;;
    "--version")
        ishare2_version "show"
        ;;
    *)
        ishare2_quick_help
        ;;
    esac
}

main() {
    check_updates
    if [[ "$1" = "test" ]]; then
        # Tests without checking if user is root
        connection_tests
        exit 0
    fi
    check_user_is_root
    check_ishare2_dir
    ishare2_sources
    ishare2_version
    check_config "$1"
    check_motd
    set_max_log_lines
    set_yml_template_folder_location
    selector "$1" "$2" "$3" "$4"
}

main "$@"
